using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using OpenComposite.Base.Designer;
using OpenComposite.Repository.Properties;
using OpenComposite.EII.Repository;
using OpenComposite.EII.Services;

namespace OpenComposite.EII.Designer.SCA
{
	public class VerbSymbol : DesignerControl, IConnector, IConnectorFactory, IRepositoryItemSymbol
	{
		#region Initialization
		public VerbSymbol()
		{
			init();
		}
		private void init()
		{
			this.BeginUpdate();

			_operation = null;
			_lstRoutes = new List<Route>();

			// this
			this.Size = new Size(20, 20);

			// _penBorder
			_penBorder = new Pen(Color.Blue);

			// _brushBack
			setBackgroundBrush();

			// _textbox
			_textbox = new NoClipTextBox();
			_textbox.AutoSize = false;
			_textbox.Bounds = new Rectangle(-50, 20, 120, 40);
			_textbox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
			_textbox.Text = this.Text;
			_textbox.TextChanged += new EventHandler(_textbox_TextChanged);

			this.ContextMenuStrip.Items.Clear();
			//this.ContextMenuStrip.Items.Add("Subscription Mapping...", null,
			//    new EventHandler(SubscriptionMapping_Click));
			Bitmap bmpDelete = Resources.delete;
			bmpDelete.MakeTransparent(Color.Magenta);
			this.ContextMenuStrip.Items
				.Add("Remove Capability", null, new EventHandler(this.removeCapability_Click));
			this.ContextMenuStrip.Items
				.Add("Delete Capability", bmpDelete, new EventHandler(this.deleteCapability_Click));

			this.Controls.Add(_textbox);

			this.EndUpdate();
		}

		#endregion

		#region Public Members

		#region Properties
		// new
		public Color BorderColor
		{
			get { return _penBorder.Color; }
			set
			{
				if ( _penBorder.Color != value ) {
					_penBorder.Color = value;
					this.Update();
				}
			}
		}

		public Repository.Operation Operation
		{
			get { return _operation; }
			set
			{
				if ( _operation != value ) {
					Repository.Operation oldOp = _operation;
					_operation = value;
					OnOperationChanged(oldOp, _operation);
				}
			}
		}

		// override
		public override bool CanFocused
		{
			get
			{
				return true;
			}
		}
		public override bool CanMove
		{
			get
			{
				return true;
			}
		}
		public override bool CanResize
		{
			get
			{
				return false;
			}
		}
		public override bool CanSelect
		{
			get
			{
				return true;
			}
		}
		public override bool UseGrid
		{
			get
			{
				return true;
			}
		}

		public override string Text
		{
			get
			{
				return _textbox.Text;
			}
			set
			{
				_textbox.Text = value;
			}
		}
		#endregion

		#region Events
		public event EventHandler OperationChanged = null;
		#endregion

		#region Methods
		// override
		public override void Paint(System.Windows.Forms.PaintEventArgs pe)
		{
			if ( !this.Visible )
				return;

			Rectangle rpaint = this.GetPaintBounds();

			// paint circle
			pe.Graphics.FillEllipse(this._brushBack, rpaint);
			pe.Graphics.DrawEllipse(_penBorder, rpaint);

			base.Paint(pe);
		}
		public override void Dispose()
		{
			dispose(_penBorder);
			dispose(_textbox);
			base.Dispose();
		}
		public override object GetDragObject(Point location, System.Windows.Forms.MouseButtons button)
		{
			return this;
		}
		public override void DropObject(object obj, Point location, System.Windows.Forms.MouseButtons button)
		{
			if ( button == System.Windows.Forms.MouseButtons.Right ) {
				//if ( obj is OrchestrationComponentSymbol || obj is ReferenceSymbol ) {
				//    this.DesignerViewControl.AddRoute((DesignerControl)obj, this);
				//} else if ( obj is InterfaceSymbol ) {
				OrchestrationComponentSymbol ocs = obj as OrchestrationComponentSymbol;
				InterfaceSymbol ifs = obj as InterfaceSymbol;
				ExposedCapabilitySymbol ecs = obj as ExposedCapabilitySymbol;
				if ( ocs != null || ifs != null || ecs != null ) {
					int inputBO, outputBO, exposedVerbID;
					//IRepositoryItem parentItem = null;
					Verb composedVerb = null;
					Repository.Workflow workflow = null;
					if ( _operation.ExposedVerbID > 0 ) {
						exposedVerbID = _operation.ExposedVerbID;
						inputBO = _operation.ExposedVerb.InputBusinessObjectID;
						outputBO = _operation.ExposedVerb.OutputBusinessObjectID;
					} else {
						exposedVerbID = -1;
						inputBO = _operation.InputBusinessObjectId;
						outputBO = _operation.OutputBusinessObjectId;
					}
					if ( ocs != null ) {
					} else if ( ifs != null ) {
						if ( ifs.Operations.Count == 1 ) {
							if ( ifs.Operations[0].WorkflowId > 0 ) {
								workflow = ifs.Operations[0].GetWorkflow();
								RepositoryService rs = (RepositoryService)this.GetService(typeof(RepositoryService));
								if ( rs == null ) throw new NullReferenceException("RepositoryService missing.");
								composedVerb = rs.GetItem<Verb>(workflow.ExtendedProperties.WorkflowStartCapabilityMethodID);
							} else {
								composedVerb = ifs.Operations[0].ExposedVerb;
							}
						} else {
						}
					} else if ( ecs != null && ecs.Parent != this.Parent ) {
						Verb vMatch = null;
						foreach ( Verb v in ecs.Verbs ) {
							if ( v.ID == exposedVerbID ) {
								composedVerb = v;
								break;
							}
							if ( v.InputBusinessObjectID == inputBO && v.OutputBusinessObjectID == outputBO ) {
								vMatch = v;
							}
						}
						if ( composedVerb == null ) composedVerb = vMatch;
						if ( composedVerb != null ) {
							WorkflowReferenceSymbol wrs = ecs.Parent as WorkflowReferenceSymbol;
							if ( wrs != null ) {
								workflow = wrs.RepositoryItem as Repository.Workflow;
							}
						}
					}
					if ( composedVerb == null ) {
						Forms.FormSelectRepositoryEx frm = new OpenComposite.EII.Forms.FormSelectRepositoryEx(
							_operation, inputBO, outputBO);
						frm.AllowedTypes = new ItemType[] { ItemType.Verb };
						if ( _operation.ExposedVerbID > 0 && _operation.ExposedVerb.CapabilityID > 0 ) {
							frm.ParentItem = _operation.ExposedVerb.Capability;
						}
						frm.MultiSelect = false;
						frm.SelectedItem = this.Operation.ComposedVerb;
						if ( frm.ShowDialog(this.DesignerViewControl.TopLevelControl as IWin32Window) == DialogResult.OK &&
							 frm.SelectedItem != null ) {
							this.Operation.ComposedVerb = frm.SelectedItem as Verb;
							this.DesignerViewControl.AddRoute(this, (DesignerControl)obj);
						}
					} else {
						this.Operation.BeginEdit();
						this.Operation.ComposedVerb = composedVerb;
						if ( workflow != null ) {
							this.Operation.WorkflowId = workflow.ID;
						}
						this.Operation.EndEdit(true);
						this.DesignerViewControl.AddRoute(this, (DesignerControl)obj);
					}
				}
			}
		}
		public override bool IsDropAllowed(object obj, Point location, System.Windows.Forms.MouseButtons button)
		{
			ExposedCapabilitySymbol ecs = obj as ExposedCapabilitySymbol;
			if ( ecs != null ) {
				// search for matching capability method (verb)
				Verb vMatch = null;
				Verb vExposed = this.Operation.ExposedVerb;
				if ( vExposed == null ) return false;
				if ( ecs.Verbs != null ) {
					foreach ( Verb verb in ecs.Verbs ) {
						if ( vExposed.ID == verb.ID ||
							 ( vExposed.InputBusinessObjectID == verb.InputBusinessObjectID &&
							  vExposed.OutputBusinessObjectID == verb.OutputBusinessObjectID ) ) {
							vMatch = verb;
							break;
						}
					}
				}
				return ( vMatch != null );
			} else {
				return this.IsDropAllowed(obj.GetType(), location, button);
			}
		}
		public override bool IsDropAllowed(Type type, Point location, System.Windows.Forms.MouseButtons button)
		{
			if ( button == System.Windows.Forms.MouseButtons.Right ) {
				return ( type == typeof(OrchestrationComponentSymbol) ||
						 type == typeof(InterfaceSymbol) ||
						 type == typeof(ExposedCapabilitySymbol) );
			} else {
				return false;
			}
		}

		public override void ReadXml(System.Xml.XmlReader reader)
		{
			RepositoryService repSvc = (RepositoryService)this.GetService(typeof(RepositoryService));
			if ( repSvc == null )
				throw new NullReferenceException("RepositoryService not available.");

			System.ComponentModel.BooleanConverter bc = new System.ComponentModel.BooleanConverter();
			try {
				while ( reader.Read() ) {
					switch ( reader.Name ) {
						case "DesignerControl":
							if ( reader.NodeType == System.Xml.XmlNodeType.EndElement )
								return;
							break;
						case "Operation":
							if ( reader.MoveToAttribute("id") && reader.ReadAttributeValue() ) {
								this.RepositoryItem = repSvc.GetItem<Operation>(int.Parse(reader.Value));
							}
							break;
						case "DesignerControls":
							// read child controls
							ReadChildControls(reader);
							break;
						default:
							ReadStandardProperties(reader);
							break;
					}
				}
			} finally {

			}
		}
		public override void WriteXml(System.Xml.XmlWriter writer)
		{
			System.ComponentModel.BooleanConverter bc = new System.ComponentModel.BooleanConverter();
			WriteStandardProperties(writer);

			if ( _operation != null ) {
				writer.WriteStartElement("Operation");
				writer.WriteAttributeString("id", _operation.ID.ToString());
				writer.WriteAttributeString("name", _operation.Name);
				writer.WriteEndElement();
			}

			WriteChildControls(writer);
		}

		public override object GetService(Type serviceType)
		{
			if ( _operation != null )
				return _operation.GetService(serviceType);
			else
				return base.GetService(serviceType);
		}
		#endregion

		#endregion

		#region Protected Members

		#region Methods
		// new
		protected void CalculateRouteDockDirection(Rectangle rStart, Rectangle rEnd,
			out RouteDockDirection dockStart, out RouteDockDirection dockEnd)
		{
			dockStart = RouteDockDirection.Right;
			dockEnd = RouteDockDirection.Left;
			try {
				int x1 = rStart.X, y1 = rStart.Y, h1 = rStart.Height, w1 = rStart.Width;
				int x2 = rEnd.X, y2 = rEnd.Y, h2 = rEnd.Height, w2 = rEnd.Width;
				int dx = 30, dy = 30;

				// t1    |     | dx |
				// ------|-----|----|------
				// t2    |    _|_   |  dy
				// ------|---|_1_|h1|------     
				// t3    |     w1   |
				// ------|-----|----|------
				// t4    |     |    |
				//    l1 | l2  | l3 | l4
				bool t1 = ( y2 + h2 + dy < y1 );
				bool t4 = ( y2 > y1 + h1 + dy );
				bool t2 = !t1 && ( y2 + h2 < y1 + h1 / 2 );
				bool t3 = !t4 && ( y2 > y1 + h1 / 2 );
				bool l1 = ( x2 + w2 + dx < x1 );
				bool l4 = ( x2 > x1 + w1 + dx );
				bool l2 = !l1 && ( x2 + w2 < x1 + w1 / 2 );
				bool l3 = !l4 && ( x2 > x1 + w1 / 2 );

				if ( l1 ) {
					dockStart = RouteDockDirection.Left;
					dockEnd = RouteDockDirection.Right;
				} else if ( l4 ) {
					dockStart = RouteDockDirection.Right;
					dockEnd = RouteDockDirection.Left;
				} else if ( t1 ) {
					dockStart = RouteDockDirection.Top;
					dockEnd = RouteDockDirection.Bottom;
				} else if ( t4 ) {
					dockStart = RouteDockDirection.Bottom;
					dockEnd = RouteDockDirection.Top;
				} else if ( t2 && l2 ) {
					dockStart = RouteDockDirection.Left;
					dockEnd = RouteDockDirection.Bottom;
				} else if ( t3 && l2 ) {
					dockStart = RouteDockDirection.Left;
					dockEnd = RouteDockDirection.Top;
				} else if ( t3 && l3 ) {
					dockStart = RouteDockDirection.Right;
					dockEnd = RouteDockDirection.Top;
				} else if ( t2 && l3 ) {
					dockStart = RouteDockDirection.Right;
					dockEnd = RouteDockDirection.Bottom;
				}
			} catch { }
		}
		protected virtual void OnOperationChanged(Repository.Operation oldOp, Repository.Operation newOp)
		{
			EventHandler tmp = this.OperationChanged;
			if ( tmp != null )
				tmp(this, EventArgs.Empty);
		}

		// override
		protected override void OnLocationChanged(EventArgs e)
		{
			setBackgroundBrush();
			base.OnLocationChanged(e);
		}
		protected override void OnSizeChanged(EventArgs e)
		{
			setBackgroundBrush();
			base.OnSizeChanged(e);
		}
		protected override void OnParentChanged(EventArgs e)
		{
			setBackgroundBrush();
			base.OnParentChanged(e);
		}
		protected override void OnParentLocationChanged()
		{
			setBackgroundBrush();
			base.OnParentLocationChanged();
		}
		protected override void OnParentSizeChanged()
		{
			setBackgroundBrush();
			base.OnParentSizeChanged();
		}
		#endregion

		#endregion

		#region Private Members

		#region Variables
		private NoClipTextBox _textbox;
		private Pen _penBorder;
		private LinearGradientBrush _brushBack;

		private Repository.Operation _operation;

		private List<Route> _lstRoutes;
		#endregion

		#region Methods
		private Point getDefaultDockPoint(RouteDockDirection dock)
		{
			Rectangle bounds = this.GetPaintBounds();
			Point p = new Point();
			switch ( dock ) {
				case RouteDockDirection.Top:
					p.X = bounds.Left + ( ( bounds.Right - bounds.Left ) / 2 );
					p.Y = bounds.Top;
					break;
				case RouteDockDirection.Bottom:
					p.X = bounds.Left + ( ( bounds.Right - bounds.Left ) / 2 );
					p.Y = bounds.Bottom;
					break;
				case RouteDockDirection.Right:
					p.X = bounds.Right;
					p.Y = bounds.Top + ( ( bounds.Bottom - bounds.Top ) / 2 );
					break;
				case RouteDockDirection.Left:
					p.X = bounds.Left;
					p.Y = bounds.Top + ( ( bounds.Bottom - bounds.Top ) / 2 );
					break;
			}
			return p;
		}
		private void setBackgroundBrush()
		{
			dispose(_brushBack);
			_brushBack = new LinearGradientBrush(
				this.GetPaintBounds(),
				Color.FromArgb(160, 60, 160), Color.FromArgb(250, 200, 250),
				LinearGradientMode.ForwardDiagonal);
		}
		#endregion

		#region Event Handlers
		private void _textbox_TextChanged(object sender, EventArgs e)
		{
			base.Text = _textbox.Text;
		}

		private void route_Removing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			e.Cancel = true;
		}
		private void controlEnd_Removed(object sender, EventArgs e)
		{
			this._operation.BeginEdit();
			this._operation.ComposedVerb = null;
			this._operation.WorkflowId = -1;
			this._operation.EndEdit(true);
			DesignerControl dc = sender as DesignerControl;
			if ( dc != null ) {
				dc.Removed -= new EventHandler(this.controlEnd_Removed);
			}
		}

		private void removeCapability_Click(object sender, EventArgs e)
		{
			if ( _operation.ExposedVerb != null ) {
				IList<IRepositoryItem> parents = Global.Data.GetRepositoryHierarchyParentsList(_operation.ExposedVerb);
				if ( parents.Count == 1 ) {
					ToolStripItem tsi = sender as ToolStripItem;
					string msg = string.Format(
						"You can't remove this capability from the last service.{0}" +
						"But it is possible to delete the capability.{0}{0}" +
						"Do you want to delete the capability \"{1}\"?", Environment.NewLine, this.Text);
					if ( MessageBox.Show(msg, tsi.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Stop)
							== DialogResult.Yes ) {
						if ( this.Delete(false) ) {
							this.Remove();
						}
					}
					return;
				}
			}
			this.Remove();
		}
		private void deleteCapability_Click(object sender, EventArgs e)
		{
			if ( this.Delete(true) ) {
				this.Remove();
			}
		}

		#endregion


		#endregion

		#region IConnectorFactory Members
		public virtual bool CreateRouteStart(DesignerControl controlEnd,
			out IConnector connector, out LineCap cap, out CustomLineCap customcap,
			out RouteDockDirection dockStart, out RouteDockDirection dockEnd,
			out bool useStartConnectorAsRouteStart)
		{
			Rectangle rS = this.GetPaintBounds();
			Rectangle rE = controlEnd.GetPaintBounds();
			//CalculateRouteDockDirection(rS, rE, out dockStart, out dockEnd);
			if ( controlEnd is ExposedCapabilitySymbol && controlEnd.Parent == this.Parent ) {
				dockStart = RouteDockDirection.Left;
				dockEnd = RouteDockDirection.Right;
			} else {
				dockStart = RouteDockDirection.Right;
				dockEnd = RouteDockDirection.Left;
				if ( controlEnd is InterfaceSymbol || controlEnd is ExposedCapabilitySymbol ) {
					controlEnd.Removed += new EventHandler(controlEnd_Removed);
				} else if ( controlEnd is CapabilitySymbol ) {
				}
			}
			connector = this;
			cap = LineCap.NoAnchor;
			customcap = null;
			useStartConnectorAsRouteStart = false;
			return true;
		}

		public virtual bool CreateRouteEnd(DesignerControl controlStart,
			out IConnector connector, out LineCap cap, out CustomLineCap customcap,
			out RouteDockDirection dockStart, out RouteDockDirection dockEnd,
			out bool useEndConnectorAsRouteEnd)
		{
			Rectangle rS = controlStart.GetPaintBounds();
			Rectangle rE = this.GetPaintBounds();
			//CalculateRouteDockDirection(rS, rE, out dockStart, out dockEnd);
			if ( controlStart is ExposedCapabilitySymbol ) {
				dockStart = RouteDockDirection.Right;
				dockEnd = RouteDockDirection.Left;
			} else {
				dockStart = RouteDockDirection.Left;
				dockEnd = RouteDockDirection.Right;
			}
			connector = this;
			cap = LineCap.NoAnchor;
			customcap = null;
			useEndConnectorAsRouteEnd = false;
			return true;
		}
		#endregion
		#region IConnector Members

		public virtual Point GetRouteStart(RouteDockDirection dock, Route route)
		{
			//if ( !_lstRoutes.Contains(route) ) {
			//    _lstRoutes.Add(route);
			//    route.Removing += new System.ComponentModel.CancelEventHandler(route_Removing);
			//}
			return getDefaultDockPoint(dock);
		}

		public virtual Point GetRouteEnd(RouteDockDirection dock, Route route)
		{
			if ( !_lstRoutes.Contains(route) ) {
				_lstRoutes.Add(route);
				route.Removing += new System.ComponentModel.CancelEventHandler(route_Removing);
			}
			return getDefaultDockPoint(dock);
		}

		#endregion

		#region IRepositoryItemSymbol Members

		public bool SaveHierarchy
		{
			get { return false; }
		}

		private bool _isReference = false;
		public bool IsReference
		{
			get { return _isReference; }
			set { _isReference = value; }
		}

		public bool CanBeMainControl
		{
			get { return false; }
		}

		public OpenComposite.EII.Repository.IRepositoryItem RepositoryItem
		{
			get
			{
				return _operation;
			}
			set
			{
				if ( value is Verb ) {
					Verb v = (Verb)value;
					RepositoryService rs = v.GetService(typeof(RepositoryService)) as RepositoryService;
					Debug.Assert(rs != null);
					IRepositoryItem parentItem = null;
					_operation = rs.GetNewItem<Operation>(false, ref parentItem);
					_operation.BeginEdit();
					_operation.Name = v.Name;
					_operation.Description = v.Description;
					_operation.SetInputBusinessObject(v.InputBusinessObject);
					_operation.SetOutputBusinessObject(v.OutputBusinessObject);
					_operation.ExposedVerb = v;
					_operation.EndEdit(true);
				} else {
					Debug.Assert(( value is Repository.Operation || value == null ), "Wrong Type");
					if ( _operation != null && _operation.ExposedVerb != null ) {
						_operation.ExposedVerb.ItemChanged -= ExposedVerb_ItemChanged;
					}
					_operation = value as Repository.Operation;
					if ( _operation != null && _operation.ExposedVerb != null ) {
						_operation.ExposedVerb.ItemChanged += ExposedVerb_ItemChanged;
					}
				}
				if ( _operation != null ) {

					if ( _operation.Name != Resources.sNewOperation ) {
						this.Text = _operation.Name;
					} else {
						_operation.Name = this.Text;
					}
				}

			}
		}

		void ExposedVerb_ItemChanged(object sender, RepositoryChangedEventArgs e)
		{
			if ( e.Member != null && e.Member.Name == "Name" && e.NewValue != null ) {
				this.Text = e.NewValue.ToString();
				_textbox.Refresh();
			}
		}

		public void Load(int idRepository, int idDomain)
		{

		}

		public bool Save(int idRepository, int idDomain)
		{
			if ( _operation == null )
				return false;

			return _operation.Save();
		}

		public bool Delete(bool bAsk)
		{
			if ( _operation == null )
				return true;

			return _operation.Delete(bAsk);
		}

		#endregion
	}
}
